package com.iknight.theory.controller;

import com.iknight.mgr.core.constant.IkgConstantCode;
import com.iknight.mgr.core.utils.IkgNumberUtils;
import com.iknight.mgr.core.utils.IkgStringUtils;
import com.iknight.mgr.core.utils.IkgWebUtils;
import com.iknight.mgr.core.websocket.WebSocketDto;
import com.iknight.mgr.core.websocket.WebSocketListener;
import com.iknight.theory.api.*;
import com.iknight.theory.model.*;
import com.iknight.theory.tree.EquipTreeNode;
import com.iknight.theory.tree.EquipTreeUtils;
import com.sun.tools.internal.xjc.reader.xmlschema.BindGreen;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;


@RestController
@RequestMapping("/rest/theory")
@Api("理论线损计算实体类")
public class TheoryCalController {
    private final static Logger logger = LogManager.getLogger(TheoryCalController.class);

    //    @Autowired
//    private TeTranService<TETranModel, String> teTranService;
//    @Autowired
//    private TeLineService<TELineModel, String> teLineService;
//    @Autowired
//    private TeLineSegService<TELineSegModel, String> teLineSegService;
//    @Autowired
//    private TeLineConsService<TELineConsModel, String> teLineConsService;
//    @Autowired
//    private TeContainerService<TEContainerModel, String> teContainerService;
    @Autowired
    private TeRecordService<TPRecordModel, String> teRecordService;
    @Autowired
    private TheoryCalService theoryCalService;

    private StringBuffer calLog;

    @PostMapping("/calculate")
    @ApiOperation("标杆值计算方法")
    public Object calculate(@RequestBody Map<String, Object> params, HttpServletRequest request, HttpServletResponse response) {
        if (null == params) {
            return IkgWebUtils.postIkgResponse(false, "传入参数为空", IkgConstantCode.PARAMSNULL);
        }

        String code = IkgStringUtils.ObjectToString(params.get("code"));

        double avgVolt = IkgNumberUtils.objectToDouble(params.get("avgVolt"));
        double calCycle = IkgNumberUtils.objectToDouble(params.get("calCycle"));
        double consVolt = IkgNumberUtils.objectToDouble(params.get("consVolt"));
        double consLength = IkgNumberUtils.objectToDouble(params.get("consLength"));
        double tgStruArgs = IkgNumberUtils.objectToDouble(params.get("tgStruArgs"));
        String tgType = IkgStringUtils.ObjectToString(params.get("tgType"));//台区类型

        calLog = new StringBuffer();
        WebSocketDto webSocketDto = WebSocketListener.getWebsocketMap().get(code);
        if (null == webSocketDto) {
            return IkgWebUtils.postIkgResponse(false, "数据通信通道未建立,请重试。", IkgConstantCode.UNESTABLISHED);
        }

        boolean checkSuccess;

        String msg;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");

        String tranId = IkgStringUtils.ObjectToString(params.get("tran_id"));
        String record_id = IkgStringUtils.getUUID();//数据存储主键

        long start = Calendar.getInstance().getTimeInMillis();//用于计算时长

        msg = "开始进行数据完整性校验。";
        printAndRecord(code, msg);

//        1.档案校验
        Map checkResult = (Map) theoryCalService.validateArchive(tranId, code);
        checkSuccess = (boolean) checkResult.get("success");
        if (!checkSuccess) {
            return IkgWebUtils.postIkgResponse(false, "档案校验失败，计算终止。", IkgConstantCode.CALCULATEABORT);
        }

        TETranModel tranModel = (TETranModel) checkResult.get("tran");
        double tranPower = IkgNumberUtils.objectToDouble(checkResult.get("tranPower"));
        double consPower = IkgNumberUtils.objectToDouble(checkResult.get("consPower"));
        double powerFactor = IkgNumberUtils.objectToDouble(checkResult.get("powerFactor"));
        List<Map> consLinels = (List<Map>) checkResult.get("consLinels");
        List<Map> consPowerls = (List<Map>) checkResult.get("consPowerls");
        List<Map> segls = (List<Map>) checkResult.get("segls");

        //2.计算单相平均电流
        //正向有功电量
        BigDecimal bdTranPower = roundValue(tranPower, 4);
        //计算电压
        BigDecimal bdAvgVolt = roundValue(avgVolt, 4);
        //时间范围
        BigDecimal bdCalCycle = roundValue(calCycle, 4);
        //功率因数
        BigDecimal bdTranPowerFactor = roundValue(powerFactor, 4);
        //计算平均电流
        BigDecimal bdIav = theoryCalService.calSinglePhaseCurrent(bdTranPower, bdTranPowerFactor, bdAvgVolt, bdCalCycle);

        Map currentResult = (Map) theoryCalService.calThreePhaseUnbalance(tranId);

        BigDecimal bdJSIav = new BigDecimal(0);
        BigDecimal bdJSMax = new BigDecimal(0);
        BigDecimal bdK1 = new BigDecimal(1);
        BigDecimal bdK2 = new BigDecimal(1);
        BigDecimal bdK3 = new BigDecimal(1);

        if (!currentResult.isEmpty()) {
            bdJSIav = roundValue(IkgNumberUtils.objectToDouble(currentResult.get("avg")), 4);
            bdJSMax = roundValue(IkgNumberUtils.objectToDouble(currentResult.get("max")), 4);
            bdK1 = roundValue(IkgNumberUtils.objectToDouble(currentResult.get("k1")), 4);
            bdK2 = roundValue(IkgNumberUtils.objectToDouble(currentResult.get("k2")), 4);
            bdK3 = roundValue(IkgNumberUtils.objectToDouble(currentResult.get("k3")), 4);
        }
        BigDecimal bdIndexMax = new BigDecimal(1);
        if (tgType.equals("0")) {
            bdIndexMax = new BigDecimal(1.2);
        } else if (tgType.equals("1")) {
            bdIndexMax = new BigDecimal(1.25);
        } else if (tgType.equals("2")) {
            bdIndexMax = new BigDecimal(1.3);
        }
        BigDecimal bdLoadIndex = theoryCalService.calLoadCurseIndex(bdJSIav, bdIav, bdIndexMax);

        msg = "时间跨度为" + calCycle + "小时,计算得到首端平均负荷电流[" + bdIav + "A]";
        printAndRecord(code, msg);
        /****************完成单相电流计算******************************************/

        Map resultCaledSegs = (Map) theoryCalService.calSegsPower(record_id, segls, code);
        List<SegPowerInfo> lineSegPowerInfols = (List<SegPowerInfo>) resultCaledSegs.get("lineSegPower");
        List<EquipTreeNode> lineTreeNodes = (List<EquipTreeNode>) resultCaledSegs.get("lineTreenodes");

        List<Map> fixedPolePowers = (List<Map>) theoryCalService.analyzeLinePoles(consLinels, consPowerls);

        msg = "开始进行线路分段等值电阻与分段损耗计算。";
        printAndRecord(code, msg);

        //计算等值电阻及线段损耗
        //获取分段下全部杆塔挂载表箱的电量
        for (int i = 0; i < lineSegPowerInfols.size(); i++) {
            String segId = lineSegPowerInfols.get(i).getSeg_id();
            //通过遍历获得分段下的全部杆塔，杆塔与计量箱关联，从而获得分段电量合计
            BigDecimal tempPower = new BigDecimal("0").setScale(4, BigDecimal.ROUND_HALF_UP);

            //遍历获得杆塔
            List<EquipTreeNode> tempNodes = EquipTreeUtils.getFixedRootNode(segId, lineTreeNodes);
            List<String> poleIdls = new ArrayList<>(new HashSet<>(iteratorTree(tempNodes.get(0))));

            //遍历获得电量合计
            for (String poleId : poleIdls) {
                for (Map m : fixedPolePowers) {
                    if (m.get("pole_id").equals(poleId)) {
                        Object _value = m.get("power_value");
                        if (null != _value) {
                            tempPower = tempPower.add(new BigDecimal(_value.toString()));
                        }
                    }
                }
            }

            logger.info("tempPower:" + tempPower);
            lineSegPowerInfols.get(i).setRest_power(tempPower.doubleValue());

            double result = Math.pow(tempPower.doubleValue(), 2) / Math.pow(consPower, 2) * lineSegPowerInfols.get(i).getUnit_resistance();
            BigDecimal actResistance = new BigDecimal(result).setScale(8, BigDecimal.ROUND_HALF_UP);

            logger.info("等值电阻：" + actResistance);
            msg = "线段长度" + lineSegPowerInfols.get(i).getSeg_length()
                    + "m,基准电阻[" + lineSegPowerInfols.get(i).getUnit_resistance()
                    + "Ω],等值电阻[" + actResistance + "Ω],";
            lineSegPowerInfols.get(i).setEq_resistance(actResistance.doubleValue());//实际电阻

            BigDecimal powerLoss = calSegLoss(tgStruArgs, bdIav, actResistance, bdLoadIndex, new BigDecimal(calCycle));

            lineSegPowerInfols.get(i).setAct_loss(powerLoss.setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue());
            logger.info("损失电量：" + powerLoss.setScale(4, BigDecimal.ROUND_HALF_UP));
            msg += " 损失电量[" + powerLoss.setScale(4, BigDecimal.ROUND_HALF_UP) + "kWh].";
            printAndRecord(code, msg);
        }

        //计算输出电量
        //P输送  P售出
        //全额损耗
        BigDecimal totalLoss = new BigDecimal(0);
        for (SegPowerInfo info : lineSegPowerInfols) {
            totalLoss = totalLoss.add(roundValue(info.getAct_loss(), 4));
        }

        msg = "线路分段基准损失电量合计:" + totalLoss + "kWh";
        printAndRecord(code, msg);


        //计算下户线损耗
        BigDecimal bdContainerLineLoss = calContainerLineLoss(consLinels, consVolt, calCycle, consLength, code);
        msg = "下户线损失电量[" + bdContainerLineLoss + "kWh].";
        printAndRecord(code, msg);

        //总损失电量
        BigDecimal bdTotalLoss = totalLoss.add(bdContainerLineLoss);
        BigDecimal bdK1TotalLoss = bdK1.multiply(totalLoss).add(bdContainerLineLoss).setScale(4, BigDecimal.ROUND_HALF_UP);
        BigDecimal bdK2TotalLoss = bdK2.multiply(totalLoss).add(bdContainerLineLoss).setScale(4, BigDecimal.ROUND_HALF_UP);
        BigDecimal bdK3TotalLoss = bdK3.multiply(totalLoss).add(bdContainerLineLoss).setScale(4, BigDecimal.ROUND_HALF_UP);
        BigDecimal bdK1LossRate = theoryCalService.calLossRate(bdK1TotalLoss, bdTranPower);
        BigDecimal bdK2LossRate = theoryCalService.calLossRate(bdK2TotalLoss, bdTranPower);
        BigDecimal bdK3LossRate = theoryCalService.calLossRate(bdK3TotalLoss, bdTranPower);

        msg = "三相平衡时，线路损失总电量为[" + bdTotalLoss + "kWh]";
        printAndRecord(code, msg);
        msg = "三相不平衡时，其中，";
        printAndRecord(code, msg);
        msg = "当一相负荷重，两相负荷轻时，线路损失总电量为[" + bdK1TotalLoss + "kWh]";
        printAndRecord(code, msg);
        msg = "当一相负荷中，一相负荷轻，一相负荷平均时，线路损失总电量为[" + bdK2TotalLoss + "kWh]";
        printAndRecord(code, msg);
        msg = "当一相负荷轻，两相负荷重时，线路损失总电量为[" + bdK3TotalLoss + "kWh]";
        printAndRecord(code, msg);
        //计算变压器损耗

        //计算电表损耗
        msg = "开始进行线路分段输送/售出电量计算。";
        printAndRecord(code, msg);

        BigDecimal bdConsPower = IkgNumberUtils.roundDouble(consPower, 4);
        lineSegPowerInfols = (List<SegPowerInfo>) theoryCalService.analyzeLineSegsPower(bdTranPower, bdConsPower, segls, lineSegPowerInfols);

        msg = "计算结束，正进行数据入库";
        printAndRecord(code, msg);
        /**
         * 入库
         */
        TPowerTranModel powerTran = new TPowerTranModel();
        powerTran.setRecord_id(record_id);
        powerTran.setTran_id(tranId);
        powerTran.setData_date(tranModel.getSection_date());
        powerTran.setPower_input(tranPower);
        powerTran.setPower_output(roundValue(consPower, 4).doubleValue());
        powerTran.setPower_loss(bdTotalLoss.doubleValue());

        BigDecimal lossRate = bdTotalLoss.divide(roundValue(tranPower, 4), 4, BigDecimal.ROUND_HALF_UP);
        powerTran.setPower_rate(lossRate.multiply(roundValue(100, 4)).doubleValue());
        powerTran.setCal_date(sdf.format(Calendar.getInstance().getTime()));
        powerTran.setCal_log(calLog.toString());

        //明细数据
        TPTranPDeModel tpTranPDeModel = new TPTranPDeModel();
        tpTranPDeModel.setObj_id(IkgStringUtils.getUUID());
        tpTranPDeModel.setRecord_id(record_id);
        tpTranPDeModel.setLoss_baseseg(totalLoss.doubleValue());
        tpTranPDeModel.setLoss_consline(bdContainerLineLoss.doubleValue());
        tpTranPDeModel.setCurrent_avg(bdJSIav.doubleValue());
        tpTranPDeModel.setCurrent_max(bdJSMax.doubleValue());
        tpTranPDeModel.setValue_k1(bdK1.doubleValue());
        tpTranPDeModel.setValue_k2(bdK2.doubleValue());
        tpTranPDeModel.setValue_k3(bdK3.doubleValue());
        tpTranPDeModel.setRate_k1(bdK1LossRate.doubleValue());
        tpTranPDeModel.setRate_k2(bdK2LossRate.doubleValue());
        tpTranPDeModel.setRate_k3(bdK3LossRate.doubleValue());
        tpTranPDeModel.setLoss_k1(bdK1TotalLoss.doubleValue());
        tpTranPDeModel.setLoss_k2(bdK2TotalLoss.doubleValue());
        tpTranPDeModel.setLoss_k3(bdK3TotalLoss.doubleValue());

        teRecordService.saveCaledTranPower(powerTran, tpTranPDeModel, lineSegPowerInfols);
        msg = "入库成功!";
        printAndRecord(code, msg);
        //计算结束
        long end = Calendar.getInstance().getTimeInMillis();
        long betweenDate = end - start;

        msg = "计算结束，用时" + betweenDate + "ms.";
        printAndRecord(code, msg);

        return IkgWebUtils.postIkgResponse(checkSuccess, null, IkgConstantCode.CALSUCCESS);
    }

    /**
     * 迭代遍历查询节点树
     *
     * @param node
     * @return
     */
    private List<String> iteratorTree(EquipTreeNode node) {
        List poleIds = new ArrayList();
        poleIds.add(node.getId());

        if (null != node) {
            for (EquipTreeNode n : (List<EquipTreeNode>) node.getNodes()) {
                poleIds.add(n.getId());
                if (n.getNodes() != null && n.getNodes().size() > 0) {
                    for (String poleId : iteratorTree(n)) {
                        poleIds.add(poleId);
                    }
                }
            }
        }
        return poleIds;
    }

    /**
     * double 转 BigDecimal 四舍五入向上
     *
     * @param scale
     * @param value
     * @return
     */
    private BigDecimal roundValue(double value, int scale) {
        return new BigDecimal(value).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 计算线路损耗电量
     *
     * @param tgStruArgs
     * @param bdIav
     * @param actResistance
     * @param bdLoadCurseIndex
     * @param dataRange
     * @return
     */
    private BigDecimal calSegLoss(double tgStruArgs, BigDecimal bdIav, BigDecimal actResistance, BigDecimal bdLoadCurseIndex, BigDecimal dataRange) {
        BigDecimal three = roundValue(tgStruArgs, 4);
        BigDecimal tenThree = new BigDecimal(10).pow(3);
        BigDecimal result = three.multiply(bdLoadCurseIndex.pow(2));
        result = result.multiply(bdIav.pow(2));
        result = result.multiply(actResistance);
        result = result.multiply(dataRange);
        result = result.divide(tenThree, 4, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 计算下户线损耗
     *
     * @param consLs     用户列表
     * @param avgLowVolt 进户线低压
     * @param calCycle   时间跨度
     * @return
     */
    private BigDecimal calContainerLineLoss(List<Map> consLs, double avgLowVolt, double calCycle, double consLength, String code) {
        BigDecimal bdAvLV = roundValue(avgLowVolt, 4);
        BigDecimal bdResult = new BigDecimal(0).setScale(4, BigDecimal.ROUND_HALF_UP);
        BigDecimal bdCalCycle = roundValue(calCycle, 4);
        String msg = null;
        for (Map map : consLs) {
            double totalPower = IkgNumberUtils.objectToDouble(map.get("totalpower"));
            if (!(totalPower >= -IkgNumberUtils.EPSINON && totalPower <= IkgNumberUtils.EPSINON)) {
                BigDecimal bdIav = roundValue(totalPower, 4).divide(bdAvLV, 4, BigDecimal.ROUND_HALF_UP);
                bdIav = bdIav.divide(roundValue(calCycle, 4), 4, BigDecimal.ROUND_HALF_UP);
                BigDecimal bdOhm = roundValue(IkgNumberUtils.objectToDouble(map.get("resistance_value")), 4);
                BigDecimal bdUnitOhm = bdOhm.divide(new BigDecimal(1000), 8, BigDecimal.ROUND_HALF_UP);

                BigDecimal bdLength = roundValue(IkgNumberUtils.objectToDouble(map.get("cl_length")), 4);
                BigDecimal bdActOhm = bdUnitOhm.multiply(bdLength).setScale(4, BigDecimal.ROUND_HALF_UP);
                BigDecimal bdLoss = bdIav.pow(2).multiply(bdActOhm).multiply(bdCalCycle);
                BigDecimal bdConsNum = roundValue(IkgNumberUtils.objectToDouble(map.get("consnum")), 4);

                BigDecimal bdConsLoss = bdIav.pow(2).multiply(new BigDecimal(1.15))
                        .multiply(bdConsNum).
                                multiply(roundValue(consLength, 4)).
                                divide(new BigDecimal(1000), 4, BigDecimal.ROUND_HALF_UP).multiply(bdCalCycle);

                bdLoss = bdLoss.add(bdConsLoss);
                bdLoss = bdLoss.divide(new BigDecimal(1000), 4, BigDecimal.ROUND_HALF_UP);
                bdLoss = bdLoss.multiply(new BigDecimal(2));
                bdLoss = bdLoss.setScale(4, BigDecimal.ROUND_HALF_UP);

                msg = "进户线编号:" + map.get("cl_no")
                        + ",长度[" + map.get("cl_length")
                        + "m],挂载用户数[" + map.get("consnum")
                        + "],单位电阻[" + bdOhm
                        + "],实际电阻[" + bdActOhm
                        + "],电量[" + totalPower
                        + "kWh],平均电流[" + bdIav
                        + "A],计算损耗[" + bdLoss + "kWh]";
                printAndRecord(code, msg);
                bdResult = bdResult.add(bdLoss);
            }
        }
        return bdResult.setScale(4, BigDecimal.ROUND_HALF_UP);
    }

    private void printAndRecord(String code, String msg) {
        SimpleDateFormat sdfms = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
        calLog.append(sdfms.format(Calendar.getInstance().getTime())).append(" -> ").append(msg).append("\n");
        IkgWebUtils.sendMsg(code, msg);
    }
}